﻿using Abp.Dependency;
using Abp.Json;
using Abp.Runtime.Caching;
using Abp.Timing;
using Microsoft.AspNetCore.Http;
using Mt.Site.Core.Base.Settings;
using Mt.Site.Core.Security;
using System;
using System.Threading.Tasks;

namespace Mt.Site.Core.Members
{
    /**
     * 缓存只是用来缓存用户信息，
     * 用户的登陆状态还是 依靠cookie来维持。
     */
    public class MembersCache : IMembersCache, ITransientDependency
    {
        private const string MemberCacheName = "app.MemberCache";
        private const string UserNameCache = "app.UserName";
        private const string CacheName = "App.Frontend.Member";
        private const string RefreshCookieKey = "RefreshCookieKey";

        /// <summary>
        /// 服务器缓存保存时长（分）
        /// </summary>
        public const int ActiveTimeout = 60;

        private static int _expireTimeSpan = 0;
        private static int _expireTimeSpan_Remember = 0;

        //默认不活动时，在线状态保留30分钟
        private int ExpireTimeSpan
        {
            get
            {
                if (_expireTimeSpan == 0)
                {
                    var str = _webConfigSettings["Front.User.Expire"];
                    int span;
                    if (str != null && int.TryParse(str, out span) && span > 0)
                    {
                        _expireTimeSpan = span;
                    }
                    else
                    {
                        _expireTimeSpan = 30; //30分钟
                    }
                }
                return _expireTimeSpan;
            }
        }
        
        //记住登陆状态，默认保存1年
        private int ExpireTimeSpan_Remember
        {
            get
            {
                if (_expireTimeSpan_Remember == 0)
                {
                    var str = _webConfigSettings["Front.User.Expire.Remember"];
                    int span;
                    if (str != null && int.TryParse(str, out span) && span > 0)
                    {
                        _expireTimeSpan_Remember = span;
                    }
                    else
                    {
                        _expireTimeSpan_Remember = 365;
                    }
                }
                return _expireTimeSpan_Remember;
            }
        }

        ///// <summary>
        ///// Cookie临近过期时不会主动延长有效时间
        ///// </summary>
        //public static bool SlidingExpiration = true;

        private ITypedCache<string, MemberCacheItem> MemberCache
            => _cacheManager.GetCache<string, MemberCacheItem>(CacheName);

        private readonly ICacheManager _cacheManager;

        private readonly IHttpContextAccessor _httpContextAccessor;

        private readonly IWebConfigSettings _webConfigSettings;

        public MembersCache(ICacheManager cacheManager, 
            IWebConfigSettings webConfigSettings, 
            IHttpContextAccessor httpContextAccessor)
        {
            _cacheManager = cacheManager;
            _httpContextAccessor = httpContextAccessor;
            _webConfigSettings = webConfigSettings;
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="memeberCacheItem">用户</param>
        /// <param name="isPersistent">是否做cookie持久化</param>
        public async Task Login(MemberCacheItem memeberCacheItem)
        {
            var context = _httpContextAccessor.HttpContext;
            if (context != null)
            {
                // 加入或更新本机的登录数据
                var strTicket = memeberCacheItem.MemberID.ToString();
                await MemberCache.SetAsync(strTicket, memeberCacheItem, TimeSpan.FromMinutes(ActiveTimeout));
                
                SetCookie(context, memeberCacheItem);
            }
        }

        public async Task Update(MemberCacheItem memeberCacheItem)
        {
            // 加入或更新本机的登录数据
            var strTicket = memeberCacheItem.MemberID.ToString();
            await MemberCache.SetAsync(strTicket, memeberCacheItem, TimeSpan.FromMinutes(ActiveTimeout));
        }

        public void Logout(Guid? memberId)
        {
            _httpContextAccessor.HttpContext.Response.Cookies.Delete(MemberCacheName);
            _httpContextAccessor.HttpContext.Response.Cookies.Delete(UserNameCache);

            if (memberId != null)
            {
                MemberCache.Remove(memberId.Value.ToString());                                          // 移除缓存     
            }
        }

        /// <summary>
        /// 通过Ticket获取用户信息
        /// </summary>
        /// <returns></returns>
        public async Task<MemberCacheItem> TryGetMemberInfo(Guid userId)
        {
            string strTicket = userId.ToString();
            var val = await MemberCache.GetAsync(strTicket, () => Task.FromResult<MemberCacheItem>(null));
   
            if (val != null)
            {
                RefreshCookie(val);
            }
            else
            {
                await MemberCache.RemoveAsync(strTicket);
            }
            
            return val;
        }

        private void SetCookie(HttpContext context, MemberCacheItem memberCacheItem)
        {
            if (memberCacheItem.IsPersistent)
            {
                var val = GetMemberCookieValue(memberCacheItem);

                context.Response.Cookies.Append(
                    MemberCacheName,
                    val,
                    new CookieOptions
                    {
                        Expires = Clock.Now.AddDays(ExpireTimeSpan_Remember),
                        Path = "/",
                        HttpOnly = true,
                        SameSite = SameSiteMode.Lax
                    }
                );

                context.Response.Cookies.Append(
                    UserNameCache,
                    memberCacheItem.LoginName,
                    new CookieOptions
                    {
                        Expires = Clock.Now.AddDays(ExpireTimeSpan_Remember),
                        Path = "/",
                        SameSite = SameSiteMode.Lax
                    }
                );
            }
            else
            {
                var val = GetMemberCookieValue(memberCacheItem);
               
                context.Response.Cookies.Append(
                    MemberCacheName,
                    val,
                    new CookieOptions
                    {
                        Expires = Clock.Now.AddMinutes(ExpireTimeSpan),
                        Path = "/",
                        HttpOnly = true,
                        SameSite = SameSiteMode.Lax
                    }
                );

                context.Response.Cookies.Append(
                    UserNameCache,
                    memberCacheItem.LoginName,
                    new CookieOptions
                    {
                        Expires = Clock.Now.AddMinutes(ExpireTimeSpan),
                        Path = "/",
                        SameSite = SameSiteMode.Lax
                    }
                );
            }
        }

        private void RefreshCookie(MemberCacheItem val)
        {
            var context = _httpContextAccessor.HttpContext;
            var feature = context.Items[RefreshCookieKey] as string;
            if(feature != null)
            {
                SetCookie(context, val);
                _httpContextAccessor.HttpContext.Items[RefreshCookieKey] = true;
            }
        }

        public MemberCookieItem GetMemberCookieItem()
        {
            string strTicket = _httpContextAccessor.HttpContext.Request.Cookies[MemberCacheName];       // 查找票据
            if (strTicket != null)
            {
                var val = SimpleStringCipherHelper.Decrypt(strTicket);

                try
                {
                    return val.FromJsonString<MemberCookieItem>();
                }
                catch
                {
                    return null;
                }
            }
            return null;
        }

        private static string GetMemberCookieValue(MemberCacheItem memeberCacheItem)
        {
            var item = new MemberCookieItem
            {
                UserId = memeberCacheItem.MemberID,
                Code = memeberCacheItem.VerificationCode,
                P = memeberCacheItem.IsPersistent
            };
            return SimpleStringCipherHelper.Encrypt(item.ToJsonString());
        }
    }
}
